package repo

import (
	"errors"
	"fmt"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cast"
	model "micro-oauth/datamodels"
	"micro-oauth/datamodels/enum"
	"micro-oauth/datamodels/params"
	"strings"
	"time"
)

func (s *ServiceImpl) GetUserById(userId uint64) *model.User {
	cacheUser := s.cacheService.GetUser(userId)
	if cacheUser != nil {
		return cacheUser
	}
	var user = new(model.User)

	err := s.db.Where("id = ?", userId).First(user).Error
	if err != nil {
		return nil
	}
	err = s.cacheService.SetUserCache(user)
	if err != nil {
		logrus.Error("set user cache error: ", err)
	}
	return user
}

func (s *ServiceImpl) GetUserByIdFromDB(userId uint64) *model.User {
	var user = new(model.User)

	err := s.db.Where("id = ?", userId).First(user).Error
	if err != nil {
		logrus.Error("get user from db error: ", err)
		return nil
	}
	return user
}

func (s *ServiceImpl) GetUserByMailFromDB(mail string) *model.User {
	var user = new(model.User)

	err := s.db.Where("email= ?", mail).First(user).Error
	if err != nil {
		logrus.Error("get user from db error: ", err)
		return nil
	}
	return user
}

func (s *ServiceImpl) AddUser(user *model.User) error {
	return s.db.Create(user).Error
}

func (s *ServiceImpl) DeleteUser(userId uint64) error {
	user := s.GetUserById(userId);if user == nil{
		return errors.New(`not found`)
	}
	user.Status = enum.UserStatus_Deleted
	now := time.Now()
	user.UpdatedAt = now
	user.DeletedAt = &now
	err := s.db.Save(user).Error
	if err != nil {
		return err
	}
	//err := s.db.Unscoped().Where(
	//	"id = ?", userId,
	//).Delete(model.User{}).Error
	//if err != nil {
	//	return err
	//}
	err = s.cacheService.DelUser(userId)
	if err != nil {
		logrus.Error("delete user cache error : ", err)
		return err
	}
	return nil
}

func (s *ServiceImpl) GetUserByName(partnerId uint64, scope_id uint64, name string) *model.User {
	var user model.User
	err := s.db.Where("partner_id = ? and name = ? and scope_id = ?", partnerId, name, scope_id).First(&user).Error
	if err != nil {
		return nil
	}
	return &user
}

func (s *ServiceImpl) ListVendorUser(req *params.VendorUserListRequest) (users []*model.User, total uint64, err error) {
	db := s.db
	db = db.Where("scope_id = ?", req.ScopeId)
	db = db.Where("type in (?)", []enum.UserType{enum.UserType_VendorManager, enum.UserType_VendorUser})
	if req.PartnerId != 0 {
		db = db.Where("partner_id = ?", req.PartnerId)
	}
	if req.Id != 0 {
		db = db.Where("id = ?", req.Id)
	}
	if len(req.Ids) != 0 {
		db = db.Where("id in (?)", req.Ids)
	}
	if req.OrderField != "" {
		orderType := req.OrderType
		if orderType == "" {
			orderType = "asc"
		}
		db = db.Order(req.OrderField + " " + orderType)
	} else {
		db = db.Order("created_at asc")
	}
	if req.Exclude.Id != 0 {
		db = db.Not("id = ?", req.Exclude.Id)
	}
	if len(req.Exclude.Ids) != 0 {
		db = db.Not("id in (?)", req.Exclude.Ids)
	}
	if req.SearchValue != "" {
		sqlStr := fmt.Sprintf("(name ilike '%%%s%%') or (email ilike '%%%s%%')", req.SearchValue, req.SearchValue)
		//vendorIds, err := metadata.SearchVendor(req.PartnerId, 10, req.SearchValue)

		var vendorIds []uint64 = make([]uint64, 0, 0)
		//vendorIds, err := metadata.SearchVendor(req.PartnerId, 10, req.SearchValue)
		if err != nil {
			logrus.WithError(err).Error("search vendor from metadata fail!")
		} else {
			if len(vendorIds) != 0 {
				vendorIdsStr := IdsToStr(vendorIds)
				sqlStr += fmt.Sprintf(" or (third_party_id in (%s))", vendorIdsStr)
			}
		}
		db = db.Where(sqlStr)
	}
	if req.Status != "" {
		db = db.Where("status = ?", req.Status)
	}
	if len(req.Type) != 0 {
		db = db.Where("type = ?", req.Type)
	}
	if len(req.VendorIds) != 0 {
		db = db.Where("third_party_id in (?)", req.VendorIds)
	}
	db.Model(&model.User{}).Count(&total)
	if req.Limit != 0 {
		db = db.Limit(req.Limit)
	}
	if req.Offset != 0 {
		db = db.Offset(req.Offset)
	}
	err = db.Find(&users).Error
	if err != nil || len(users) == 0 {
		fmt.Println("empty error :", err)
		emptyResult := make([]*model.User, 0)
		return emptyResult, 0, err
	}
	return users, total, nil
}

func (s *ServiceImpl) ListUser(req *params.UserListRequest) (users []*model.User, total uint64, err error) {
	db := DefaultListDB(s.db, &req.ListRequest)
	db = db.Where("scope_id = ?", req.ScopeId)
	if req.Name != "" {
		db = db.Where("name = ?", req.Name)
	}
	if req.Code != "" {
		db = db.Where("code = ?", req.Code)
	}
	if len(req.Statuses) != 0 {
		db = db.Where("status in (?)", req.Statuses)
	}
	if len(req.Types) != 0 {
		db = db.Where("type in (?)", req.Types)
	}
	if req.ThirdPartyId != 0 {
		db = db.Where("third_party_id = ?", req.ThirdPartyId)
	}
	if len(req.Mail) != 0 {
		db = db.Where("email = ?", req.Mail)
	}
	if len(req.BindStatus) != 0 {
		db = db.Where("bind_status in (?)", req.BindStatus)
	}
	db.Model(&model.User{}).Count(&total)
	if req.Limit != 0 {
		db = db.Limit(req.Limit)
	}
	if req.Offset != 0 {
		db = db.Offset(req.Offset)
	}
	if req.RoleId != 0 {
		db = db.Joins("join oauth_user_role_relation as ur on ur.user_id = oauth_user.id").
			Where("ur.role_id = ?", req.RoleId)
	}
	if req.WithRoles {
		db = db.Preload("Roles")
	}
	if req.WithScope {
		db = db.Preload("Scope")
	}
	err = db.Find(&users).Error
	if err != nil || len(users) == 0 {
		fmt.Println("empty error :", err)
		emptyResult := make([]*model.User, 0)
		return emptyResult, 0, err
	}
	return users, total, nil
}

func (s *ServiceImpl) UpdateUser(user *model.User) error {
	err := s.db.Save(user).Error
	if err != nil {
		return err
	}
	cacheErr := s.cacheService.SetUserCache(user)
	if cacheErr != nil {
		return cacheErr
	}
	return nil
}

func IdsToStr(ids []uint64) string {
	if len(ids) == 0 {
		return ""
	}
	idStrList := make([]string, len(ids))
	for i, id := range ids {
		idStrList[i] = cast.ToString(id)
	}
	return strings.Join(idStrList, ",")
}
